Анализа текстуалних података - припрема текста са блога и/ли сајта¶
Податке које генеришемо и/ли желимо да обрадимо и анализирамо често су у текстуалном формату (помислите само на извештаје лекара, текстове закона, текстове порука размењених на друштвеним мрежама...), због чега ћемо се у наредном сегменту позабавити и анализом текста. Ова област је изузетно велика и активна (нове методе и алгоритми генеришу се готово на дневном нивоу), те не претендујемо да овде одговоримо на сва питања у вези са текстом већ да отворимо нека почетна питања и понудимо неке од алата за једноставне анализе, које ће, надамо се, послужити као мотивација за нека будућа истраживања.
Ова радна свеска служи да се прикупе текстуални подаци са веб страна и сачувају у csv фајл тако да можемо да се бавимо анализом како смо и до сада навикли. Савладавање ове радне свеске није неопходно за праћење остатка, она више служи за инспирацију уколико будете желели да преузмете и анализирате неки други текст са интернета. У наставку ћемо показати како се:
- преузима текст са веб стране
- користе библиотеке requests и beautifulsoup за издвајање корисних и текстуалних података из html фајла
# библиотеке које ћемо користити за преузимање текста са веб страна
import requests
import bs4 as bs
# библиотека функција за баратање стринговима, од користи за анализу текста
import string
# претходно коришћене библиотеке за манипулацију подацима и цртање
import numpy as np
import matplotlib.pyplot as plt
import pandas as pd
У овој радној свесци преузећемо текстове са ових веб адреса:
У оквиру библиотеке requests налази се функција get уз помоћ које ћемо преузети садржај са жељене веб адресе:
url = 'https://petlja.org/petlja/fondacija'
stranica = requests.get(url)
#stranica.text
У stranici је сада садржај ове веб стране, а ако нас занима само текст, то можемо проверити користећи stranica.text, међутим тако ћемо бити затрпани html кодом у мање читљивом формату (пробајте!). Како би нам преузети сарджај био читљивији, од помоћи ће нам бити функција BeautifulSoup која ће дати садржај разложити препознајући html структуру и раздвојити све тагове у нове редове (упоредите садржај soup и stranica.text).
soup = bs.BeautifulSoup(stranica.text,"html.parser")
#soup
Следећа линија кода је тренутно искоментарисана зато што је њен исход велика количина html кода, која нам служи за брзу претрагу и уочавање уз који таг се налази текст са сајта који желимо да преузмемо.
Постоје алтернативни начини да ово потражите користећи свој претраживач (углавном користећи Developer алате у свом претраживачу), али овде се фокусирамо на претрагу у оквиру ове радне свеске.
Једноставно речено, циљ нам је да у прегршти текста који ће бити исписан када се наредна линија покрене, препознамо html структуру, то јест да су информације углавном оивичене таговима, нпр. <body>
и <\body>
. Циљ нам је да нађемо блок текста на српском, односно енглеском и да видимо који таг је искоришћен да тај текст означи.
#print(soup.prettify())
Претходном претрагом у позадини наше жељене веб стране дошли смо до тага уз текст који пратимо <p>
и <\p>
, као и да је атрибут који прати тај таг {'class':'mb-3'}
.
Практична ствар је што са библиотеком коју користимо не морамо да пишемо функцију која ће пронаћи сва појављивања овог тага са одређеним атрибутом, та функција већ постоји: find_all. Њеним позивањем добићемо све сегменте претходног html кода који су оивичени задатим тагом.
potencijalnitekst = soup.find_all('p',attrs={'class':'mb-3'})
#potencijalnitekst
Ако последњу линију покренете, приметићете да је potencijalnitekst заправо листа, те ћемо у наставку проверити њену дужину и преузети садржај свих елемената.
Уколико желите да анализирате текстове са неких других веб страна, претходни процес ће најчешће бити ствар коју ћете морати да измените у коду пошто различити аутори веб страна уз другачије тагове пакују текст, нпр. ако желите да преузмете текстове популарних музичких песама са нпр. овог сајта текст ћете наћи уз таг div
, ca атрибутом {'class':'lyricsbox'}
. Оба ова тага су у складу са очекивањима, p долази од речи параграф (енг. paragrah), док је div сегмент (енг. division).
Видимо да су сви параграфи текста преузети (што можемо проверити и одласком на дату веб страну), али су тагови још увек ту, и има више сегментата, текста, тачније:
len(potencijalnitekst)
Како је potencijalnitekst листа, можемо погледати садржај првог елемента:
potencijalnitekst[0]
Видимо да овај први елемент листе и даље садржи html тагове, стога ћемо користити опцију text да издвојимо текст:
potencijalnitekst[0].text
Како је ово потребно урадити за све преузете параграфе (или један када их нема више на сајту са кога преузимате текст), у наставку је пар линија кода да то аутоматизовано пречистимо. Oзнаке за нове редове и сличне редундантне размаке уклањамо користећи функцију strip.
tekst=[]
for segment in potencijalnitekst:
tekst.append(segment.text.strip())
#tekst
Коначно текст који је подељен у параграфе (који су елементи листе) можемо повезати на следећи начин користећи функцију join:
textzaanalizu_srb=' '.join(tekst)
Првих 10 карактера можемо проверити на следећи начин:
textzaanalizu_srb[:10]
У наставку ћемо истом серијом наредби преузети и исти текст на енглеском са истог сајта:
url = 'https://petlja.org/petlja/foundation/'
page = requests.get(url)
soup = bs.BeautifulSoup(page.text,"html.parser")
Добра пракса је проверити тагове (у претраживачу или овде), иако је за претпоставити да су аутори сајта решили текстове на конзистентан начин, неретко се дешава да то није тако. У овом случају, текст је поново оивичен истим таговима тако да ћемо брзо преузети све информације и на српском језику:
potencijalnitekst = soup.find_all('p',attrs={'class':'mb-3'})
#potencijalnitekst
tekst=[]
for segment in potencijalnitekst:
tekst.append(segment.text.strip())
#tekst
textzaanalizu_eng=' '.join(tekst)
textzaanalizu_eng[:10]
Како нам је циљ да дођемо до листе речи за даљу анализу, елиминисаћео све знаке интерпункције. За то ћемо направити кратку функцију у наставку.
У оквиру библиотеке string знаци интерпункције су:
string.punctuation
Додаћемо још неке знаке навода који се често појављују у текстовима а нису у претходној листи карактера:
znaci_interpunkcije = string.punctuation + '”“’‘—'
У оквиру функције у наставку, користићемо фунцкију replace да свако појављивање знака интерпункције заменимо размаком. Алтернативно, могуће је само уклонити знаке интерпункције без додавања нових размака, али због могућности да су знаци негде залепљени за речи (нпр. када није остављен размак након тачке), додаћемо размаке.
Вреди размислити о другачијем третирању карактера ', тренутно, додавањем размака уместо њега речи попут "don't" делимо на "don" и "t" - слободно поправљајте функцију.
def ukloni_znake_interpunkcije(text):
for znak in znaci_interpunkcije:
text=text.replace(znak,' ')
return text
Тестирајмо функцију на првих 150 карактера:
print(textzaanalizu_srb[:150]+'\n'+ukloni_znake_interpunkcije(textzaanalizu_srb[:150]))
Сачуваћемо верзије текста без знака интерпункције:
textzaanalizu_eng = ukloni_znake_interpunkcije(textzaanalizu_eng)
textzaanalizu_srb = ukloni_znake_interpunkcije(textzaanalizu_srb)
Коначно, можемо поделити текст на речи (групе карактера између два размака) користећи функцију split:
listareci_eng = textzaanalizu_eng.split()
listareci_srb = textzaanalizu_srb.split()
Првих пет речи у оба текста су у наставку:
listareci_srb[:5]
listareci_eng[:5]
Можемо проверити и да ли се дужине ових листа разликују:
(len(listareci_eng),len(listareci_srb))
Коначно, направићемо једноставне табеле (колони реч доделићемо садржај листе речи):
df_eng = pd.DataFrame(listareci_eng,columns={'Rec'})
df_srb = pd.DataFrame(listareci_srb,columns={'Rec'})
df_srb.head(2)
Можемо додати колону у којој је дужина речи примењујући типичну функцију за дужину len. Да бисмо је применили на садржај сваке од ћелија у колони, користимо apply:
df_eng['Duzina_reci'] = df_eng['Rec'].apply(len)
df_srb['Duzina_reci'] = df_srb['Rec'].apply(len)
Сачуваћемо ове табеле за даљу анализу:
df_eng.to_csv('petlja_eng_reci.csv',index=False)
df_srb.to_csv('petlja_srb_reci.csv',index=False)